package com.eaglefly.redis.scan;

import com.eaglefly.redis.annotation.RDao;
import com.eaglefly.redis.core.DaoProxyFactory;
import com.eaglefly.redis.key.RedisKeyGenerator;
import com.eaglefly.redis.utils.TypeUtils;
import org.redisson.client.codec.Codec;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;

/**
 * 功能：redisDao扫描类
 * 时间：2018/1/5 15:40
 *
 * @author zhaojinpeng
 */
public class ClassPathDaoScanner extends ClassPathBeanDefinitionScanner {

    /**
     * 扫描注解
     */
    private static final Class<? extends Annotation> ANNOTATION_CLASS = RDao.class;

    public ClassPathDaoScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
    }


    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (beanDefinitions.isEmpty()) {
            logger.warn("No RedisDao was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {

        DaoProxyFactory daoProxyFactory = new DaoProxyFactory();

        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (GenericBeanDefinition) holder.getBeanDefinition();

            if (logger.isDebugEnabled()) {
                logger.debug("创建RedisProxy:'" + holder.getBeanName()  + "' and '" + definition.getBeanClassName() + "' redisInterface");
            }

            String beanClassName = definition.getBeanClassName();

            Class<?> daoInterface = TypeUtils.getClassFromName(beanClassName);

            AnnotationMetadata metadata = ((ScannedGenericBeanDefinition) definition).getMetadata();
            Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ANNOTATION_CLASS.getName());

            String key = (String) annotationAttributes.get("key");
            if (key == null || "".equals(key)) {
                Class<?> keyClass = (Class<?>) annotationAttributes.get("keyGenerator");
                RedisKeyGenerator keyGenerator = TypeUtils.getNewBean(keyClass);
                key = keyGenerator.generatorKey(daoInterface);
            }

            Class<?> codecClass = (Class<?>) annotationAttributes.get("codec");
            Codec codec = TypeUtils.getNewBean(codecClass);

            definition.getPropertyValues().add("daoInterface", daoInterface);
            definition.getPropertyValues().add("superInterface", TypeUtils.getInterface(daoInterface));
            definition.getPropertyValues().add("daoProxyFactory", daoProxyFactory);
            definition.getPropertyValues().add("redisKey", key);
            definition.getPropertyValues().add("codec", codec);

            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

            // the mapper interface is the original class of the bean
            // but, the actual class of the bean is DaoFactoryBean
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            definition.setBeanClass(DaoFactoryBean.class);

        }

    }

    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        // if specified, use the given annotation and / or marker interface
        if (ClassPathDaoScanner.ANNOTATION_CLASS != null) {
            addIncludeFilter(new AnnotationTypeFilter(ClassPathDaoScanner.ANNOTATION_CLASS));
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter(new TypeFilter() {
                @Override
                public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                    return true;
                }
            });
        }

        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                return className.endsWith("package-info");
            }
        });
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

}
